package smallfrench.ui;

import java.util.ArrayList;

import javax.swing.SwingUtilities;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultStyledDocument;
import javax.swing.text.PlainDocument;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyleContext;
import javax.swing.text.TabSet;
import javax.swing.text.TabStop;

import smallfrench.moteur.lexer.Lexer;
import smallfrench.moteur.lexer.err.ErreurLexer;
import smallfrench.moteur.lexer.lexemes.Lexeme;

@SuppressWarnings("serial")
public class MDocument extends DefaultStyledDocument {

	private Lexer lexer;

	/** <code>true</code> si la coloration lexicale est activée. */
	private boolean colorationActivee = true;

	/**
	 * Le thread de l'analyse lexicale ou <code>null</code>.
	 */
	private Thread threadAnalyse;



	public MDocument() {
		lexer = new Lexer();

		putProperty(PlainDocument.tabSizeAttribute, 1);

		addDocumentListener(new DocumentListener() {
			@Override
			public void removeUpdate(DocumentEvent e) {
				maj();
			}

			@Override
			public void insertUpdate(DocumentEvent e) {
				maj();
			}

			@Override
			public void changedUpdate(DocumentEvent arg0) {

			}
		});
	}



	public void setPolice(int largeurPolice) {
		// On initialise les tabulations...

		int tailleTab = largeurPolice * 4;

		TabStop[] tabStops = new TabStop[20];

		for (int i = 0; i < tabStops.length; i++) {
			tabStops[i] = new TabStop(i * tailleTab);
		}

		TabSet tabs = new TabSet(tabStops);

		StyleContext sc = StyleContext.getDefaultStyleContext();

		AttributeSet paraSet = sc.addAttribute(
				SimpleAttributeSet.EMPTY,
				StyleConstants.TabSet, tabs);

		setParagraphAttributes(0, getLength(), paraSet, false);
	}



	public void setColorationActivee(boolean activee) {
		colorationActivee = activee;
		if (colorationActivee) {
			maj();
		} else {
			// Efface les anciens styles appliqués.
			setCharacterAttributes(0, getLength(),
					new SimpleAttributeSet(), true);
		}
	}

	public boolean getColorationActivee() {
		return colorationActivee;
	}


	/**
	 * Met à jour les styles si {@link MDocument#colorationActivee}, doit
	 * être appelée dans l'EDT.
	 */
	private void maj() {
		if (threadAnalyse != null) {
			threadAnalyse.interrupt();
		}

		if (! colorationActivee) return;

		threadAnalyse = new Thread(new Runnable() {
			@Override
			public void run() {
				maj2();
			}
		});
		threadAnalyse.run();
	}


	/** Ne doit pas être appellée dans l'EDT. */
	private void maj2() {

		ArrayList<Lexeme> lexemes;

		try {

			System.out.println("MDocument.maj2() deb " + getLength());
			lexemes = lexer.analyser(getText(0, getLength()), true, true);

		} catch (ErreurLexer e) {
			e.printStackTrace();
			return;
		} catch (BadLocationException e) {
			e.printStackTrace();
			return;
		}


		try {

			appliquerStyles(lexemes);

		} catch (IllegalStateException e) {
			System.out.println("MDocument.maj2() Erreur");
		}

		threadAnalyse = null;System.out.println("MDocument.maj2() fin");
	}

	private void appliquerStyles(final ArrayList<Lexeme> lexemes) {

		Runnable runnable = new Runnable() {
			@Override
			public void run() {

				writeLock();

				// Efface les anciens styles appliqués.
				setCharacterAttributes(0, getLength(),
						new SimpleAttributeSet(), true);

				for (Lexeme token : lexemes) {
					setLexeme(new LexemeColore(token));
				}

				writeUnlock();

			}
		};

		SwingUtilities.invokeLater(runnable);
	}


	private void setLexeme(LexemeColore lexeme) {
		setCharacterAttributes(lexeme.getPosition(), lexeme.getLongueur(),
				lexeme.getStyle(), false);
	}
}

